#ifndef _HANDY_H
#define _HANDY_H

/**************************
    Generic macro's
 **************************/
 
#define _MACRO_BEG         do {
#define _MACRO_END         } while(0)
#define _MACRO(expr)       _MACRO_BEG expr _MACRO_END

#ifndef COUNT_OF
#define COUNT_OF(x) (sizeof(x)/sizeof((x)[0]))
#endif
#ifndef ALLOC
#define ALLOC(x) ((x *)malloc(sizeof(x)))
#endif
#ifndef ALLOC_COUNT
#define ALLOC_COUNT(x,count) ((x *)malloc(sizeof(x)*(count)))
#endif
#ifndef ALLOC_STRING
#define ALLOC_STRING(len) ((char *)malloc(len+1))
#endif

/**************************
    Debug macro's
 **************************


Debugging can be enabled using "#define _DEBUG_ENABLE 1", and asserting can be enabled with _ASSERT_ENABLE.
When debugging is disabled, both _CVCALL _CCALL will still call the function, but the result will not be checked

Examples:

_TRACE; // file name and number

_TFUNC; // file name,number, and function name

_ECHO("Hello World\n");

_TECHO("Hello World");

_TVAR(i, myIntegerVariable); // show integer variable
_TVAR(s, myStringVariable); // show string variable
_TVAR(.10s, myStringVariable); // show first 10 characters of string variable

_ENTER(_TV(i,arg1);_TV(s,arg2);) // show function and print two arguments
_LEAVE(_TV(i,result);)

_DUMPVAL(ptrToObject);           // dump memory of object
_DUMP(ptr, byteLength);           // dump memory

_ASSERT(x==0); // if x is not null, show assertion.
_ASSERT_VERBOSE(x==0, _TV(i,x)); // if x is not null, show assertion and print x.
_ASSERT_EQUAL(x,0); // if x is not null, show assertion and the values.

_CCALL(func())     // checks whether func() returns 0
_CVCALL(func(),retVal)     // checks whether func() returns retVal

*****************/

// Configuration:
#define _IF_ANSI(yes,no)        no
#define _IF_AUTOFLUSH(yes,no)   no
#define _PRINTF_BEG             fprintf(stdout,
#define _VPRINTF_BEG            vfprintf(stdout,
#define _FLUSH                  fflush(stdout);fsync(fileno(stdout))

// Implementation:
#ifndef _DEBUG_ENABLE
#ifdef DBGPRINT
#define _DEBUG_ENABLE   1
#else
#define _DEBUG_ENABLE   0
#endif
#endif
#ifndef _ASSERT_ENABLE
#if defined(ASSERT) || _DEBUG_ENABLE
#define _ASSERT_ENABLE   1
#else
#define _ASSERT_ENABLE   0
#endif
#endif
#if _ASSERT_ENABLE
#include <unistd.h>
#include <stdio.h>
#define _IF_ASSERT(yes,no) yes
#else
#define _IF_ASSERT(yes,no) no
#endif
#if _DEBUG_ENABLE
#define _IF_DEBUG(yes,no) yes
#define _PRINTF1(f,p)     _MACRO_BEG _PRINTF_BEG f,p); _MACRO_END
#define _PRINTF2(f,p1,p2) _MACRO_BEG _PRINTF_BEG f,p1,p2); _MACRO_END
#else
#define _IF_DEBUG(yes,no) no
#define _PRINTF1(f,p)
#define _PRINTF2(f,p1,p2)
#endif

#define _ANSI_NORMAL       _IF_ANSI("\x1b[0m",)
#define _ANSI_ERROR(txt)   _IF_ANSI("\x1b[1;31m",) txt _ANSI_NORMAL
#define _ANSI_FAINT(txt)   _IF_ANSI("\x1b[1;36m",) txt _ANSI_NORMAL
#define _ANSI_STRONG(txt)  _IF_ANSI("\x1b[1;37m",) txt _ANSI_NORMAL

#define _ECHO(str)        _PRINTF1("%s",str)
#define _TRACE            _IF_DEBUG(_MACRO(_PRINTF2(_ANSI_FAINT("(( %s, %i ))") "\n",__FILE__,__LINE__); _IF_AUTOFLUSH(_FLUSH,);),)
#define _TLINE(prints)    _IF_DEBUG(_MACRO(prints; _ECHO(" "); _TRACE;),)
#define _TENTER(prints)    _TLINE(_PRINTF1(  "%s(",__func__ ); prints; _ECHO(")") )
#define _TLEAVE(prints)   _TLINE( _PRINTF1(  "Leaving %s: ",__func__ ); prints; )
#define _TFUNC            _TENTER()
#define _TECHO(str)       _TLINE(_ECHO(str);)
#define _TV(t,v)   		    _PRINTF2("%s = " #t ": %" #t " ",#v, v)
#define _TVAR(t,v)   		  _TLINE( _TV(t,v) )
#define _DUMP(v,l)        _TLINE( \
	int __i; unsigned char *__v=(unsigned char *)(v); unsigned char *__ev=__v+(l); _ECHO(#v "= {\n");\
	while( __v<__ev)\
	{	for(__i=0; __i<16 && __v <__ev; __i++) _PRINTF1( " %.2x", *(__v++)); \
		__v-=__i; do { _ECHO("   "); } while(__i++<16);  \
		for(__i=0; __i<16 && __v <__ev; __i++) { char __c=*(__v++); _PRINTF1("%c", __c<32?'.':__c); } _ECHO("\n"); \
	} _ECHO("}"); )
#define _DUMPVAL(v)        _DUMP(v, sizeof(*(v)))
#define _CVCALL_VERBOSE(x,retval,action) _IF_ASSERT(_MACRO(int __err=(int)(x); if (__err!=retval) { _TLINE(_PRINTF2("\"%s\" " _ANSI_ERROR("returned: 0x%x"), #x,__err); ); action; }),x)
#define _CCALL_VERBOSE(x,action)         _CVCALL_VERBOSE(x,0,action)
#define _ASSERT_VERBOSE(x,action)        _IF_ASSERT(_MACRO(if ((x)==0) { _TLINE(_ECHO("\"" #x "\" " _ANSI_ERROR("asserted.")); );  action; }),)
#define _CVCALL(x,retval)  _CVCALL_VERBOSE(x,retval,)
#define _CCALL(x)          _CCALL_VERBOSE(x,)
#define _ASSERT(x)         _ASSERT_VERBOSE(x,)
#define _ASSERT_EQUAL(x,y) _IF_ASSERT(_MACRO(if ((x)!=(y)) _TLINE(_ECHO("\"" #x "!=" #y "\" " _ANSI_ERROR("asserted:")); _PRINTF2(" 0x%x != 0x%x",(int)(x),(int)(y) ););),)
#define _DIV_BY_ZERO       _IF_ASSERT(_MACRO(int p=3; _PRINTF1("%i\n",p/(p-3));),)

#if ( ( __GNUC__ >=3 ) || ( __STDC_VERSION__ >= 199901L ))
#if _DEBUG_ENABLE
#define _PRINTF(...)      _MACRO_BEG _PRINTF_BEG __VA_ARGS__); _MACRO_END
#else
#define _PRINTF(...)
#endif
#endif

#endif
